import { COUNT_COMPLETION_REJECTED_AFTER } from "../../util/parameters"
import { AutocompleteOutcome } from "./types"

export class AutocompleteLoggingService {
	// Key is completionId
	private _abortControllers = new Map<string, AbortController>()
	private _logRejectionTimeouts = new Map<string, NodeJS.Timeout>()
	private _outcomes = new Map<string, AutocompleteOutcome>()
	_lastDisplayedCompletion: { id: string; displayedAt: number } | undefined = undefined

	public createAbortController(completionId: string): AbortController {
		const abortController = new AbortController()
		this._abortControllers.set(completionId, abortController)
		return abortController
	}

	public deleteAbortController(completionId: string) {
		this._abortControllers.delete(completionId)
	}

	public cancel() {
		this._abortControllers.forEach((abortController) => {
			abortController.abort()
		})
		this._abortControllers.clear()
	}

	public accept(completionId: string): AutocompleteOutcome | undefined {
		if (this._logRejectionTimeouts.has(completionId)) {
			clearTimeout(this._logRejectionTimeouts.get(completionId))
			this._logRejectionTimeouts.delete(completionId)
		}

		if (this._outcomes.has(completionId)) {
			const outcome = this._outcomes.get(completionId)!
			outcome.accepted = true
			this.logAutocompleteOutcome(outcome)
			this._outcomes.delete(completionId)
			return outcome
		}
		return undefined
	}

	public cancelRejectionTimeout(completionId: string) {
		if (this._logRejectionTimeouts.has(completionId)) {
			clearTimeout(this._logRejectionTimeouts.get(completionId)!)
			this._logRejectionTimeouts.delete(completionId)
		}

		if (this._outcomes.has(completionId)) {
			this._outcomes.delete(completionId)
		}
	}

	public markDisplayed(completionId: string, outcome: AutocompleteOutcome) {
		const logRejectionTimeout = setTimeout(() => {
			// Wait 10 seconds, then assume it wasn't accepted
			outcome.accepted = false
			this.logAutocompleteOutcome(outcome)
			this._logRejectionTimeouts.delete(completionId)
		}, COUNT_COMPLETION_REJECTED_AFTER)
		this._outcomes.set(completionId, outcome)
		this._logRejectionTimeouts.set(completionId, logRejectionTimeout)

		// If the previously displayed completion is still waiting for rejection,
		// and this one is a continuation of that (the outcome.completion is the same modulo prefix)
		// then we should cancel the rejection timeout
		const previous = this._lastDisplayedCompletion
		const now = Date.now()
		if (previous && this._logRejectionTimeouts.has(previous.id)) {
			const previousOutcome = this._outcomes.get(previous.id)
			const c1 = previousOutcome?.completion.split("\n")[0] ?? ""
			const c2 = outcome.completion.split("\n")[0]
			if (previousOutcome && (c1.endsWith(c2) || c2.endsWith(c1) || c1.startsWith(c2) || c2.startsWith(c1))) {
				this.cancelRejectionTimeout(previous.id)
			} else if (now - previous.displayedAt < 500) {
				// If a completion isn't shown for more than
				this.cancelRejectionTimeout(previous.id)
			}
		}

		this._lastDisplayedCompletion = {
			id: completionId,
			displayedAt: now,
		}
	}

	private logAutocompleteOutcome(outcome: AutocompleteOutcome) {
		if (!process.env.VITEST) {
			console.log(outcome)
		}
	}
}
